package com.think.admin.oauth.integration;

import com.think.admin.cons.AdminCons;
import com.think.admin.oauth.exception.CustomOauth2Exception;
import com.think.admin.oauth.integration.authenticator.IntegrationAuthenticator;
import com.think.common.utils.ApplicationContextProvider;
import com.think.common.utils.JacksonUtils;
import com.think.common.utils.ResultUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

/**
 * <p>
 * 认证拦截器
 * </p>
 *
 * @author: xwj
 * @data: 2019-05-10
 */
public class IntegrationAuthenticationFilter extends GenericFilterBean {

    /**
     * 认证器
     */
    private Collection<IntegrationAuthenticator> authenticators;

    /**
     * 只是拦截get，post的登录地址
     */
    private RequestMatcher requestMatcher;

    public IntegrationAuthenticationFilter() {
        this.requestMatcher = new OrRequestMatcher(
                new AntPathRequestMatcher(AdminCons.Oauth.LOGIN_URL, "GET"),
                new AntPathRequestMatcher(AdminCons.Oauth.LOGIN_URL, "POST")
        );
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // 只拦截特定的地址
        if (!requestMatcher.matches(request)) {
            filterChain.doFilter(request, response);
            return;
        }

        //设置集成登录信息
        IntegrationAuthentication integrationAuthentication = new IntegrationAuthentication();
        integrationAuthentication.setAuthType(request.getParameter(AdminCons.Oauth.AUTH_TYPE));
        integrationAuthentication.setAuthParameters(request.getParameterMap());
        IntegrationAuthenticationContext.set(integrationAuthentication);
        try {
            //预处理
            this.prepare(integrationAuthentication);

            filterChain.doFilter(request, response);

            //后置处理
            this.complete(integrationAuthentication);
        } catch (CustomOauth2Exception e) {
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter()
                    .write(JacksonUtils.toJson(new ResultUtil(HttpStatus.BAD_REQUEST, e.getMessage())));
        } finally {
            IntegrationAuthenticationContext.clear();
        }
    }

    /**
     * 进行预处理
     *
     * @param integrationAuthentication
     */
    private void prepare(IntegrationAuthentication integrationAuthentication) {
        //延迟加载认证器
        if (this.authenticators == null) {
            synchronized (this) {
                ApplicationContext applicationContext = ApplicationContextProvider.getApplicationContext();
                Map<String, IntegrationAuthenticator> integrationAuthenticatorMap = applicationContext
                        .getBeansOfType(IntegrationAuthenticator.class);
                if (integrationAuthenticatorMap != null) {
                    this.authenticators = integrationAuthenticatorMap.values();
                }
            }
        }

        // 如果没有就设置为空，防止空指针异常
        if (this.authenticators == null) {
            this.authenticators = new ArrayList<>();
        }

        // 预处理
        boolean prepareHandleIsSuccess = false;
        for (IntegrationAuthenticator authenticator : authenticators) {
            if (authenticator.support(integrationAuthentication)) {
                authenticator.prepare(integrationAuthentication);
                prepareHandleIsSuccess = true;
            }
        }

        // 判断验证器的匹配情况
        if (!prepareHandleIsSuccess) {
            throw new CustomOauth2Exception("验证方式不支持");
        }
    }

    /**
     * 后置处理
     *
     * @param integrationAuthentication
     */
    private void complete(IntegrationAuthentication integrationAuthentication) {
        for (IntegrationAuthenticator authenticator : authenticators) {
            if (authenticator.support(integrationAuthentication)) {
                authenticator.complete(integrationAuthentication);
            }
        }
    }
}